#ifndef CNC_COMPILER_STREAM_READER_H
#define CNC_COMPILER_STREAM_READER_H

#include <iostream>

namespace cnc {

	namespace compiler {

		class StreamReader {
			unsigned int prevColumn_;
			unsigned long row_;
			unsigned int column_;
			std::istream *inputStream_;
		protected:
			StreamReader() : inputStream_(0)
			{
			}
			int getChar()
			{
				if (inputStream_ == 0)
					return EOF;
				enum { START, ACCEPT_LF, ACCEPT_CR } state = START;
				int c;
				while ("no char found")
					switch (state) {
					case START:
						switch (c = inputStream_->get()) {
						case '\r':
							state = ACCEPT_LF;
							break;
						case '\n':
							state = ACCEPT_CR;
							break;
						default:
							column_++;
							return c;
						}
					case ACCEPT_LF:
						if ((c = inputStream_->get()) != '\n')
							inputStream_->putback(c);
					case ACCEPT_CR:
						row_++;
						prevColumn_ = column_;
						column_ = 1;
						return '\n';
					}
				return EOF;
			}
			bool putBackChar(int c)
			{
				if (inputStream_ == 0)
					return false;
				if (c != EOF)
					inputStream_->putback(c);
				else
					return false;
				if (c == '\n') {
					row_--;
					column_ = prevColumn_;
				} else
					column_--;
				return true;
			}
		public:
			void setInputStream(std::istream *inputStream)
			{
				inputStream_ = inputStream;
				resetInputStream();
			}
			void resetInputStream()
			{
				if (inputStream_ != 0) {
					inputStream_->clear();
					inputStream_->seekg(0);
				}
				row_ = 1;
				column_ = 1;
				prevColumn_ = 1;
			}
			unsigned long getRow() const
			{
				return row_;
			}
			unsigned int getColumn() const
			{
				return column_;
			}
		};

	} // namespace compiler

} // namespace cnc

#endif

